/********************************************************************************
* Project       : No Latency RTSP streaming Project
* Author        : Seo, SeokWon 
* e-mail        : kueony@gmail.com
* Licence       : See the Copyright
*********************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/poll.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/msg.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <errno.h>

#include "vll_protocol.h"
#include "vll_sc_protocol.h"
#include "vll_server_setting.h"
#include "vll_server_acceptor.h"

#include "lib_log.h"
#include "lib_common.h"
#include "lib_network.h"
#include "lib_str.h"

#define VLL_SERVER_VER		"vll_server.0.0.1(07/02/11)"
#define MSG_TIMEOUT			5000
#define INNER_MSG_TIMEOUT	30000
#define MAIN_USLEEP_TIME	1000
#define	MAX_CLIENT_CONNECT	10
#define MAX_VIDEO_DATA_SIZE 614400

#define MSET(x) memset (&(x), 0, sizeof(x))

// *****************************************
//.. variables
// *****************************************
typedef struct pollfd	st_PollFd;

typedef struct _user_info
{
	int					iSock;
	struct _user_info	*prev;
	struct _user_info	*next;
} st_UserInfo;

typedef struct _vll_server_data
{
	int						iOperationStatus;
	st_VllSetInfo			stSet;
	st_AcceptorThreadInfo	stAcceptor;
	int						iNowClientNum;
	st_UserInfo*			ClientListHead;
	st_UserInfo*			ClientListTail;	
} st_VllData;


st_VllData		g_stData;

// *****************************************
// .. functions
// *****************************************
static int			MainRoutine();
static void			Close();

static void			Signal(int a_iSigNum);
static void			InitStatus();
static int			InitConfig();
static int			InitSignal();
static int			InitAcceptThread(st_VllData* a_pstData);
static void			AddUser(st_UserInfo* a_pstUser);
static void			DelUser(st_UserInfo *a_pstUser);
static void			DelAllUser();

extern int			RunAcceptorThread(st_AcceptorThreadInfo* a_pstInfo);
extern int			CloseAcceptorThread(st_AcceptorThreadInfo* a_pstInfo);


// *****************************************
// .. main
// *****************************************
int main()
{
	LibLogSetFileName((char*)"./vll_server.log");
	LibLogSetType(LIB_LOG_TYPE_MSG | LIB_LOG_TYPE_DATA | LIB_LOG_TYPE_ERROR | LIB_LOG_TYPE_DEBUG);

	LogMsg(LIB_LOG_TYPE_MSG, "(%s,%d) %s", __FILE__, __LINE__, VLL_SERVER_VER);

	InitStatus();

	if(InitConfig() < 0) 
	{
		LogMsg(LIB_LOG_TYPE_ERROR, "(%s,%d) %s", __FILE__, __LINE__, "config");
		goto RUN_FAIL;
	}else LogMsg(LIB_LOG_TYPE_MSG, "(%s,%d) %s", __FILE__, __LINE__, "initialize_setup_ok");

/*
	if(InitDaemon() < 0)
	{
		LogMsg(LIB_LOG_TYPE_ERROR, "(%s, %d) %s", __FILE__, __LINE__, "fail_initdaemon");
		goto RUN_FAIL;
	}else LogMsg(LIB_LOG_TYPE_MSG, "(%s,%d) %s", __FILE__, __LINE__, "initialize_deamon_ok");
*/

	InitSignal();

	if(InitAcceptThread(&g_stData) < 0) // .. memory free required !!
	{
		LogMsg(LIB_LOG_TYPE_ERROR, "(%s,%d) %s", __FILE__, __LINE__, "acceptor");
		goto RUN_FAIL;
	}else LogMsg(LIB_LOG_TYPE_MSG, "(%s,%d) %s", __FILE__, __LINE__, "initialize_acceptor_pk");

	LogMsg(LIB_LOG_TYPE_MSG, "(%s, %d) %s", __FILE__, __LINE__, "success_initialize_all");	

	MainRoutine();

	Close();
	return 0;

RUN_FAIL:
	Close();
	exit(0);

	return 0;
}


static void Close()
{
	SOCKET_ALL_CLOSE(g_stData.stAcceptor.iaSock[0]);
	SOCKET_ALL_CLOSE(g_stData.stAcceptor.iaSock[1]);
	CloseAcceptorThread(&g_stData.stAcceptor);
	LogMsg(LIB_LOG_TYPE_MSG, "(%s, %d) %s", __FILE__, __LINE__, "close_acceptor");

	DelAllUser();
	
	LogMsg(LIB_LOG_TYPE_MSG, "(%s, %d) %s", __FILE__, __LINE__, "close_vll_server");
	
	return;
}


static int MainRoutine()
{
	st_PollFd			staPoll[2];
	st_VllMsg			stMsg;
	int					iResult;
	int					i;
	int					iSockCount = 1;
	int					iVideoSourceSock = 0;
	st_ServerClientMsg	stVideoMsg;
	char				caVideoData[MAX_VIDEO_DATA_SIZE];	
	st_UserInfo			*pstUser		= NULL;
	st_UserInfo			*pstNextUser	= NULL;

	staPoll[0].fd		= g_stData.stAcceptor.iaSock[0];
	staPoll[0].events	= POLLIN;

	g_stData.iOperationStatus = 1;

	while(g_stData.iOperationStatus == 1)
	{
		iResult = poll(staPoll, iSockCount, MAIN_USLEEP_TIME);	// NON-BLOCK mode (TimeOut)

		if(iResult < 0)	// Poll Error
		{
			if(g_stData.iOperationStatus == 0) break;

			g_stData.iOperationStatus = 0;
			LogMsg(LIB_LOG_TYPE_ERROR, "(%s, %d) %s", __FILE__, __LINE__, "fail_poll");
			LogMsg(LIB_LOG_TYPE_ERROR, "(%s, %d) %s", __FILE__, __LINE__, "stop_all_operation");
			break;			
		}
		else if(iResult == 0) // Poll Timeout
		{
			continue;
		}
		
		for(i = 0 ; i < iSockCount ; i++)
		{
			if(staPoll[i].revents & (POLLNVAL | POLLHUP | POLLERR))
			{
				if(i == 0) 
				{
					LogMsg(LIB_LOG_TYPE_ERROR, "(%s, %d) %s", __FILE__, __LINE__, "err_acceptor");
					g_stData.iOperationStatus = 0;
					break;
				}
				else
				{
					// close video source sock
					iSockCount = 1;
					SOCKET_ALL_CLOSE(iVideoSourceSock);
				}
			}

			if(staPoll[i].revents & POLLIN)
			{
				if(i == 0) // new message from acceptor
				{
					bzero(&stMsg, sizeof(stMsg));
					
					LogMsg(LIB_LOG_TYPE_MSG, "(%s, %d) %s", __FILE__, __LINE__, "new_msg_from_acceptor");
				
					if(RecvMsg4FixSize(staPoll[i].fd, (char *)&stMsg, sizeof(stMsg)) < 0)
					{
						LogMsg(LIB_LOG_TYPE_ERROR, "(%s, %d) %s", __FILE__, __LINE__, "fail_get_msg_from_acceptor");
						g_stData.iOperationStatus = 0;
						break;
					}
					
					if(stMsg.opcode == VLL_LOGIN_USER) // add new user
					{
						st_UserInfo*	pstUser = NULL;

						if(g_stData.iNowClientNum >= MAX_CLIENT_CONNECT)
						{
							LogMsg(LIB_LOG_TYPE_ERROR, "(%s, %d) %s", __FILE__, __LINE__, "max_cilent");
							SOCKET_ALL_CLOSE(stMsg.sock);
							continue;
						}

						pstUser = NULL;
						pstUser = (st_UserInfo*) calloc (1, sizeof(st_UserInfo));
						
						pstUser->iSock	= stMsg.sock;

						if(pstUser == NULL)
						{
							LogMsg(LIB_LOG_TYPE_ERROR, "(%s, %d) %s", __FILE__, __LINE__, "calloc_new_user");
							SOCKET_ALL_CLOSE(stMsg.sock);
							continue;
						}

						AddUser(pstUser);
						LogMsg(LIB_LOG_TYPE_MSG, "(%s, %d) %s", __FILE__, __LINE__, "add_user");
					}
					else // connect video source
					{
						iVideoSourceSock	= stMsg.sock;
						staPoll[1].fd		= iVideoSourceSock;
						staPoll[1].events	= POLLIN;
						iSockCount			= 2;
						
						LogMsg(LIB_LOG_TYPE_MSG, "(%s, %d) %s", __FILE__, __LINE__, "connected_video_source");
					}
				}
				else // video data
				{
					if(RecvMsg4FixSize(staPoll[i].fd, (char *)&stVideoMsg, sizeof(stVideoMsg)) < 0)
					{
						LogMsg(LIB_LOG_TYPE_ERROR, "(%s, %d) %s", __FILE__, __LINE__, "fail_get_msg_from_acceptor");
						break;
					}
					
					if(RecvMsg4FixSize(staPoll[i].fd, (char *) caVideoData, stVideoMsg.datasize) < 0)
					{
						LogMsg(LIB_LOG_TYPE_ERROR, "(%s, %d) %s", __FILE__, __LINE__, "fail_get_msg_from_acceptor");
						break;
					}
					
					// send all client this data
					for(pstUser	= g_stData.ClientListHead; pstUser != NULL ; )
					{
						pstNextUser = pstUser->next;

						if(SendMsg4FixSize(pstUser->iSock, (char *)&stVideoMsg, sizeof(stVideoMsg)) < 0)
						{
							LogMsg(LIB_LOG_TYPE_ERROR, "(%s, %d) %s", __FILE__, __LINE__, "send_video_msg1");
							DelUser(pstUser);
							continue;
						}
						
						if(SendMsg4FixSize(pstUser->iSock, (char *) caVideoData, stVideoMsg.datasize) < 0)
						{
							LogMsg(LIB_LOG_TYPE_ERROR, "(%s, %d) %s", __FILE__, __LINE__, "send_video_msg1");
							DelUser(pstUser);
							continue;
						}						

						pstUser = pstNextUser;
					}
				}
			}
		}
	}
	
	return 0;
}

static void InitStatus()
{
	bzero(&g_stData, sizeof(st_VllData));

	g_stData.iOperationStatus	= 0;

	return;
}

static int InitConfig()
{
	SetInitInfo(&g_stData.stSet);

	if(strlen(g_stData.stSet.caServIp) < 1) return -1;
	if(g_stData.stSet.iServPort < 1) return -1;

	return 0;
}


// ****************************************************
// Set action for (SIGINT, SIGUSR1, SIGUSR2, SIGSEGV)
// ****************************************************
static void Signal(int a_iSigNum)
{
	LogMsg(LIB_LOG_TYPE_DEBUG, "(%s, %d) get_signal=%d", __FILE__, __LINE__, a_iSigNum);

	switch(a_iSigNum)
	{
		case SIGINT:
			g_stData.iOperationStatus = 0;
			break;
		case SIGSEGV:
			abort();
			break;
		case SIGUSR1:	
			g_stData.iOperationStatus = 0;
			break;
		case SIGUSR2:
			break;
	}
	
	return;
}


// ****************************************************
// Ingnore all signal and Allow (SIGINT, SIGUSR1, SIGUSR2, SIGSEGV)
// ****************************************************
static int InitSignal()
{
	struct sigaction	stAct;
    sigset_t			sigBlockMask;
	int					i;

	for(i = 0 ; i < 65 ; i++)
	{
		signal(i, SIG_IGN);
	}
	
	sigfillset(&sigBlockMask);
    
	stAct.sa_handler  = Signal;
    stAct.sa_mask     = sigBlockMask;
    stAct.sa_flags    = SA_RESTART;

    sigaction(SIGINT, &stAct, NULL);
    sigaction(SIGUSR1, &stAct, NULL);
    sigaction(SIGUSR2, &stAct, NULL);
    sigaction(SIGSEGV, &stAct, NULL);

	return 0;
}


// ****************************************************
// Initialize Acceptor Thread  ** memory free required
// ****************************************************
static int InitAcceptThread(st_VllData* a_pstData) 
{
	int		iaSock[2];

	if(socketpair(AF_UNIX, SOCK_STREAM, AF_LOCAL, iaSock) != 0)
	{
		LogMsg(LIB_LOG_TYPE_ERROR, "(%s, %d) %s", __FILE__, __LINE__, "fail_create_socketpair_for_acceptor_thread>");
		return -1;
	}

	// .. initialize variable of acceptor thread
	a_pstData->stAcceptor.Thread_t			= (pthread_t) 0;
	a_pstData->stAcceptor.iOperationStatus	= 0;
	a_pstData->stAcceptor.iaSock[0]			= iaSock[0];
	a_pstData->stAcceptor.iaSock[1]			= iaSock[1];
	a_pstData->stAcceptor.pcServIp			= a_pstData->stSet.caServIp;
	a_pstData->stAcceptor.iServPort			= a_pstData->stSet.iServPort;
	a_pstData->stAcceptor.iServSock			= 0;

	if(RunAcceptorThread(&a_pstData->stAcceptor) < 0)
	{
		LogMsg(LIB_LOG_TYPE_ERROR, "(%s, %d) %s", __FILE__, __LINE__, "fail_run_accept_thread");
		SOCKET_ALL_CLOSE(a_pstData->stAcceptor.iaSock[0]);
		SOCKET_ALL_CLOSE(a_pstData->stAcceptor.iaSock[1]);
		bzero(&a_pstData->stAcceptor, sizeof(st_AcceptorThreadInfo));
		return -1;
	}

	return 0;
}




// ****************************************************
// Add new user
// ****************************************************
static void AddUser(st_UserInfo* a_pstUser)
{
	if(a_pstUser == NULL) return;

	if(g_stData.ClientListHead == NULL)
	{
		g_stData.ClientListHead	= a_pstUser;
		g_stData.ClientListTail	= a_pstUser;
		a_pstUser->prev			= NULL;
		a_pstUser->next			= NULL;
	}
	else
	{
		a_pstUser->prev					= g_stData.ClientListTail;
		a_pstUser->next					= NULL;
		g_stData.ClientListTail->next	= a_pstUser;
		g_stData.ClientListTail			= a_pstUser;
	}

	g_stData.iNowClientNum++;

	return;
}


// ****************************************************
// Delete user
// ****************************************************
static void DelUser(st_UserInfo *a_pstUser)
{
	if(a_pstUser == NULL) return;
	
	SOCKET_ALL_CLOSE(a_pstUser->iSock);

    if(a_pstUser == g_stData.ClientListHead)
    {
        g_stData.ClientListHead = a_pstUser->next;
    }
    else if(a_pstUser == g_stData.ClientListTail)
    {
        g_stData.ClientListTail 		= a_pstUser->prev;
        g_stData.ClientListTail->next	= NULL;
    }
    else
    {
        a_pstUser->prev->next	= a_pstUser->next;
        a_pstUser->next->prev	= a_pstUser->prev;
    }

	g_stData.iNowClientNum--;

	free(a_pstUser);

	return;
}

// ****************************************************
// Delete All User
// ****************************************************
static void DelAllUser()
{
	st_UserInfo	*pstUser		= NULL;
	st_UserInfo	*pstNextUser	= NULL;

	if(g_stData.ClientListHead == NULL) return;

	LogMsg(LIB_LOG_TYPE_DEBUG, "(%s, %d) %s", __FILE__, __LINE__, "del_all_Client");

	for(pstUser	= g_stData.ClientListHead; pstUser != NULL ; )
	{
		pstNextUser = pstUser->next;

		LogMsg(LIB_LOG_TYPE_DEBUG, "(%s, %d) %s", __FILE__, __LINE__, "del_Client");
		DelUser(pstUser);

		pstUser = pstNextUser;
	}

	return;
}




